package com.sjd.framework.aspectj;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.sjd.common.annotation.lock.LocalLock;
import com.sjd.common.constant.HttpStatus;
import com.sjd.common.exception.ServiceException;
import com.sjd.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 本地锁aop
 *
 * @author rr
 */
@Aspect
@Configuration
@Slf4j
public class LockMethodAspect {
    private static final Cache<String, Object> CACHES = CacheBuilder.newBuilder()
            // 最大缓存1000个
            .maximumSize(1000)
            // 设置写缓存5秒过期
            .expireAfterWrite(5, TimeUnit.SECONDS)
            .build();

    @Around("execution(public * *(..)) && @annotation(com.sjd.common.annotation.lock.LocalLock)")
    public Object interceptor(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        LocalLock localLock = method.getAnnotation(LocalLock.class);
        String key;
        if (localLock.isExpress()) {
            key = getKey(localLock.key(), joinPoint.getArgs());
        } else {
            key = localLock.key();
        }
        if (StringUtils.isNotEmpty(key)) {
            if (CACHES.getIfPresent(key) != null) {
                throw new ServiceException(localLock.errMsg(), HttpStatus.CONFLICT);
            }
            CACHES.put(key, key);
        }
        try {
            return joinPoint.proceed();
        } catch (Throwable e) {
            log.error("本地锁加锁异常，异常原因：" + e.getMessage());
            throw new ServiceException("本地锁加锁出现异常", HttpStatus.ERROR);
        } finally {
            // 出现异常后，使key失效
            CACHES.invalidate(key);
        }
    }

    /**
     * key 的生成策略
     *
     * @param keyExpress 表达式
     * @param args       参数
     * @return 最总生成的key
     */
    private String getKey(String keyExpress, Object[] args) {
        if (Objects.isNull(args) || args.length == 0) {
            return keyExpress;
        }
        for (int i = 0; i < args.length; i++) {
            keyExpress = keyExpress.replace("args[" + i + "]", args[i].toString());
        }
        return keyExpress;
    }

}
